#pragma once

#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>

enum class BoundNodeKind : int
{
    // expression;
    ErrorExpression,
    LiteralExpression,
    UnaryExpression,
    BinaryExpression,
    VariableExpression,
    AssignmentExpression,

    // statements;
    IfStatement,
    ForStatement,
    GotoStatement,
    LabelStatement,
    WhileStatement,
    BlockStatement,
    ExpressionStatement,
    VariableDeclaration,
    ConditionalGotoStatement,
};

class BoundNode;

typedef std::shared_ptr<BoundNode> SharedBoundNode;

class BoundNode
{
public:
public:
    BoundNodeKind GetKind() { return this->Kind; }
    std::string   GetKindName() { return this->KindAllName[this->Kind]; }

    virtual std::vector<SharedBoundNode>                      GetChildren() { return {}; }
    virtual std::vector<std::tuple<std::string, std::string>> GetProperties() { return {}; }

    // 输出 PrettyPrint Tree;
    void WriteTo(std::ostream &os) { PrettyPrint(os, this); }

    // 输出 PrettyPrint Tree;
    std::string ToString()
    {
        std::stringstream os;
        this->WriteTo(os);
        return os.str();
    }

protected:
    BoundNode(BoundNodeKind kind) : Kind(kind) {}

private:
    BoundNodeKind                               Kind;
    static std::map<BoundNodeKind, std::string> KindAllName;

private:
    void static PrettyPrint(std::ostream &os, BoundNode *node, std::string indent = "", bool isLast = true);
    static std::string GetText(BoundNode *node);
};
